﻿using DevExpress.Blazor;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Blazor.Components.Models;
using DevExpress.ExpressApp.Blazor.SystemModule;
using DevExpress.ExpressApp.Blazor.Templates.Navigation.ActionControls;
using DevExpress.ExpressApp.Blazor.Templates.Security.ActionControls;
using DevExpress.ExpressApp.Blazor.Templates.Toolbar.ActionControls;
using DevExpress.ExpressApp.DC;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Templates;
using DevExpress.ExpressApp.Templates.ActionControls;
using DevExpress.Persistent.Base;
using DevExpress.Utils.CommonDialogs.Internal;
using Microsoft.AspNetCore.Components;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace EasyXaf.BlazorTabbedMdi;

public class MdiMainWindowTemplate : MdiViewWindowTemplate, ISupportActionsToolbarVisibility, IMdiFrameTemplate
{
    private WindowGroup _currentWindowGroup;

    protected Window MainWindow => ViewWindow;

    public override bool CanWindowKeepOpen => CanShowTabbedMdi || ((IModelApplicationMdi)Application.Model).IsWindowKeepOpen;

    public bool CanShowTabbedMdi => !IsMobile && ((IModelApplicationMdi)Application.Model).IsShowTabbedMdi;

    public bool CanEnableMdiGroup => CanShowTabbedMdi && ((IModelApplicationMdi)Application.Model).EnableMdiGroup;

    public bool CanShowCloseAction => CanWindowKeepOpen && !CanShowTabbedMdi;

    public override bool HasUnsavedChanges => WindowGroups.Any(g => g.HasUnsavedChanges);

    public Window CurrentWindow
    {
        get => CurrentWindowGroup.CurrentWindow;
        private set
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var windowGroup = GetWindowGroup(value);
            if (windowGroup != null)
            {
                windowGroup.SetCurrentWindow(value);
                CurrentWindowGroup = windowGroup;
            }
        }
    }

    public WindowGroup CurrentWindowGroup
    {
        get => _currentWindowGroup ??= DefaultWindowGroup;
        private set => _currentWindowGroup = value;
    }

    public WindowGroup DefaultWindowGroup => WindowGroups.First(g => g.IsDefaultGroup);

    public IList<Window> CurrentGroupWindows => CurrentWindowGroup.Windows;

    public IEnumerable<Window> AllWindows => WindowGroups.SelectMany(g => g.Windows);

    public ObservableCollection<WindowGroup> WindowGroups { get; }

    public DxToolbarAdapter HeaderToolbar { get; }

    public NavigateBackActionControl NavigateBackActionControl { get; }

    public AccountComponentAdapter AccountComponent { get; }

    public ShowNavigationItemActionControl ShowNavigationItemActionControl { get; }

    public string AboutInfoString { get; set; }

    public MdiMainWindowTemplate(Window window)
        : base(window)
    {
        NavigateBackActionControl = new NavigateBackActionControl();
        AddActionControl(NavigateBackActionControl);

        AccountComponent = new AccountComponentAdapter();
        AddActionControls(AccountComponent.ActionControls);

        ShowNavigationItemActionControl = new ShowNavigationItemActionControl();
        AddActionControl(ShowNavigationItemActionControl);

        WindowGroups = new ObservableCollection<WindowGroup>();
        WindowGroups.CollectionChanged += WindowGroups_CollectionChanged;
        WindowGroups.Add(new WindowGroup { Name = WindowGroup.DefaultGroupName });

        HeaderToolbar = new DxToolbarAdapter(new DxToolbarModel() { CssClass = "pe-2" });
        HeaderToolbar.AddActionContainer(nameof(PredefinedCategory.QuickAccess), ToolbarItemAlignment.Right);
        HeaderToolbar.AddActionContainer(nameof(PredefinedCategory.Notifications), ToolbarItemAlignment.Right);
        HeaderToolbar.AddActionContainer(nameof(PredefinedCategory.Diagnostic), ToolbarItemAlignment.Right);

        var showNavigationItemController = window.GetController<ShowNavigationItemController>();
        if (showNavigationItemController != null)
        {
            showNavigationItemController.CustomShowNavigationItem += ShowNavigationItemController_CustomShowNavigationItem;
        }

        ViewChanged += MdiMainWindowTemplate_ViewChanged;

        if (CanWindowKeepOpen)
        {
            MainWindow.SetControllerActive<ConfirmationActionRegistrationController>(false);

            foreach (var controller in MainWindow.Controllers)
            {
                if (controller is ViewController)
                {
                    controller.Active[MainWindow.GetType().ToString()] = false;
                }
            }
        }
    }

    protected static string GetViewKey(View view)
    {
        return view is DetailView
            ? $"{view.Id}_{view.ObjectSpace.GetKeyValueAsString(view.CurrentObject)}"
            : view.Id;
    }

    protected string GetViewGroupName(View view)
    {
        var groupName = string.Empty;

        if (CanEnableMdiGroup)
        {
            if (string.IsNullOrWhiteSpace(groupName) && view.Model is IModelViewMdi model)
            {
                groupName = model.MdiGroupName;
                if (string.IsNullOrWhiteSpace(groupName) && view is ObjectView objectView)
                {
                    if (objectView.ObjectTypeInfo.FindMember(nameof(model.MdiGroupName)) is IMemberInfo memberInfo)
                    {
                        if (objectView.CurrentObject != null)
                        {
                            groupName = memberInfo.GetValue(objectView.CurrentObject) as string;
                        }
                    }
                    if (string.IsNullOrWhiteSpace(groupName))
                    {
                        groupName = objectView.ObjectTypeInfo.FullName;
                    }
                }
            }
        }

        if (string.IsNullOrWhiteSpace(groupName))
        {
            groupName = WindowGroup.DefaultGroupName;
        }

        return groupName;
    }

    protected WindowGroup GetWindowGroup(string name)
    {
        return WindowGroups.FirstOrDefault(x => x.Name == name);
    }

    protected WindowGroup GetWindowGroup(Window window)
    {
        return WindowGroups.FirstOrDefault(g => g.Windows.Contains(window));
    }

    protected Window CreateWindow(View view)
    {
        var childWindow = Application.CreateWindow(TemplateContext.Undefined, null, true, false, view);
        childWindow.ViewChanged += ChildWindow_ViewChanged;

        MdiViewWindowTemplate childWindowTemplate = null;

        if (view.Model is IModelViewMdi model && model.MdiWindowTemplateType != null)
        {
            if (typeof(MdiViewWindowTemplate).IsAssignableFrom(model.MdiWindowTemplateType))
            {
                childWindowTemplate = (MdiViewWindowTemplate)Activator.CreateInstance(model.MdiWindowTemplateType, childWindow);
            }
        }

        childWindowTemplate ??= new MdiViewWindowTemplate(childWindow);
        childWindowTemplate.IsMobile = IsMobile;
        childWindowTemplate.IsWindowKeepOpen = CanWindowKeepOpen;
        childWindowTemplate.CaptionChanged += WindowTemplate_CaptionChanged;
        childWindow.SetTemplate(childWindowTemplate);
        childWindow.SetView(view);

        return childWindow;
    }

    protected void RemoveWindow(Window window)
    {
        DisposeableWindows.Add(window);
        window.ViewChanged -= ChildWindow_ViewChanged;

        var windowGroup = GetWindowGroup(window);
        if (windowGroup != null)
        {
            var index = windowGroup.CurrentWindowIndex;
            windowGroup.Windows.Remove(window);
            windowGroup.CurrentWindowIndex = index;
        }
    }

    protected Window GetWindow(string viewKey)
    {
        return WindowGroups
            .SelectMany(g => g.Windows.Where(w => GetViewKey(w.View) == viewKey))
            .FirstOrDefault();
    }

    protected Window OpenWindow(View view)
    {
        var window = GetWindow(GetViewKey(view));
        if (window == null)
        {
            window = CreateWindow(view);
            window.View.Closed += (s, e) =>
            {
                RemoveWindow(window);
            };

            var groupName = GetViewGroupName(view);
            var windowGroup = GetWindowGroup(groupName);
            windowGroup ??= new WindowGroup { Name = groupName };
            windowGroup.Windows.Add(window);

            if (!WindowGroups.Contains(windowGroup))
            {
                WindowGroups.Add(windowGroup);
            }
        }

        return window;
    }

    protected Window OpenWindow(ViewShortcut viewShortcut)
    {
        var viewKey = viewShortcut.ViewId;
        var viewModel = Application.FindModelView(viewShortcut.ViewId);
        if (viewModel is IModelDetailView)
        {
            viewKey = $"{viewShortcut.ViewId}_{viewShortcut.ObjectKey}";
        }

        var window = GetWindow(viewKey);
        if (window != null)
        {
            ShowWindow(window);
        }

        return window;
    }

    public async Task<bool> CloseWindowsAsync(List<Window> windows, bool forceClose = false)
    {
        var canClose = true;
        if (windows.Any(w => w.Template is IMdiFrameTemplate t && t.HasUnsavedChanges) && !forceClose)
        {
            if (await ShowConfirmationDialogAsync() == DialogResult.Yes)
            {
                canClose = false;
            }
        }

        if (canClose)
        {
            if (windows.Count == AllWindows.Count())
            {
                windows = windows.Skip(1).ToList();
            }

            windows.ForEach(w => RemoveWindow(w));

            if (CurrentGroupWindows.Any())
            {
                OnViewChanged();
            }
            else
            {
                ShowWindow(AllWindows.First());
            }
        }

        return canClose;
    }

    public Task<bool> CloseWindowAsync(Window window, bool forceClose = false)
    {
        return CloseWindowsAsync(new List<Window> { window }, forceClose);
    }

    public Task<bool> CloseLeftWindowsAsync(Window window)
    {
        return CloseWindowsAsync(CurrentGroupWindows.Take(CurrentGroupWindows.IndexOf(window)).ToList());
    }

    public Task<bool> CloseRightWindowsAsync(Window window)
    {
        return CloseWindowsAsync(CurrentGroupWindows.Skip(CurrentGroupWindows.IndexOf(window) + 1).ToList());
    }

    public Task<bool> CloseOtherWindowsAsync(Window window)
    {
        return CloseWindowsAsync(CurrentGroupWindows.Where(w => w != window).ToList());
    }

    public Task<bool> CloseAllWindowsAsync()
    {
        return CloseWindowsAsync(CurrentGroupWindows.ToList());
    }

    public void ShowWindow(Window window)
    {
        ((IFrameTemplate)this).SetView(window.View);
    }

    public void OnActiveTabIndexChanged(int index)
    {
        if (index >= 0 && index < CurrentGroupWindows.Count)
        {
            ShowWindow(CurrentGroupWindows[index]);
        }
    }

    public void ShowWindowListDialog()
    {
        var windowList = new MdiWindowList();
        AllWindows.ToList().ForEach(w => windowList.Windows.Add(new MdiWindow(w)));
        var objectSpace = Application.CreateObjectSpace(typeof(MdiWindowList));
        var view = Application.CreateDetailView(objectSpace, windowList);
        Application.ShowViewStrategy.ShowViewInPopupWindow(view);
    }

    void IFrameTemplate.SetView(View view)
    {
        if (view != null && view != View)
        {
            if (CanWindowKeepOpen)
            {
                CurrentWindow = OpenWindow(view);
            }
            else
            {
                if (CurrentWindow != null)
                {
                    DisposeableWindows.Add(CurrentWindow);
                }

                if (CurrentGroupWindows.Any())
                {
                    foreach (var window in CurrentGroupWindows)
                    {
                        if (!DisposeableWindows.Contains(window))
                        {
                            DisposeableWindows.Add(window);
                        }
                    }
                    CurrentGroupWindows.Clear();
                }

                var currentWindow = CreateWindow(view);
                CurrentGroupWindows.Add(currentWindow);
                currentWindow.SetCloseActionActive(currentWindow.View is not ListView);
                CurrentWindow = currentWindow;
            }

            if (CurrentWindow.View != view)
            {
                DisposeableViews.Add(view);
            }

            SetView(CurrentWindow.View);
            OnViewChanged();
        }
    }

    protected override void Window_ViewChanging(object sender, ViewChangingEventArgs e)
    {
        e.DisposeOldView = false;
    }

    private void ChildWindow_ViewChanged(object sender, ViewChangedEventArgs e)
    {
        if (e.SourceFrame?.View != null)
        {
            ((IFrameTemplate)this).SetView(e.SourceFrame.View);
        }
    }

    private void WindowTemplate_CaptionChanged(object sender, EventArgs e)
    {
        OnStateChanged();
    }

    private void WindowGroups_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            if (e.NewItems != null && e.NewItems.Count > 0 && e.NewItems[0] is WindowGroup windowGroup)
            {
                windowGroup.Windows.CollectionChanged += Windows_CollectionChanged;
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            if (e.OldItems != null && e.OldItems.Count > 0 && e.OldItems[0] is WindowGroup windowGroup)
            {
                windowGroup.Windows.CollectionChanged -= Windows_CollectionChanged;
            }
        }
    }

    private void Windows_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        var windows = WindowGroups.First(g => g.Windows == sender).Windows;

        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            if (CanShowCloseAction && windows.Count == 2)
            {
                windows[0].SetCloseActionActive(true);
            }
            windows.Last().SetCloseActionActive(CanShowCloseAction);
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            if (CanShowCloseAction && windows.Count == 1)
            {
                windows[0].SetCloseActionActive(false);
            }
        }
    }

    private void MdiMainWindowTemplate_ViewChanged(object sender, EventArgs e)
    {
        if (MainWindow.View != CurrentWindow.View)
        {
            MainWindow.SetView(CurrentWindow.View);
        }
    }

    private void ShowNavigationItemController_CustomShowNavigationItem(object sender, CustomShowNavigationItemEventArgs e)
    {
        if (e.ActionArguments.SelectedChoiceActionItem.Data is ViewShortcut viewShortcut)
        {
            if (OpenWindow(viewShortcut) != null)
            {
                e.Handled = true;
            }
        }
    }

    protected override RenderFragment CreateComponent() => MdiMainWindowTemplateComponent.Create(this);

    protected override IEnumerable<IActionControlContainer> GetActionControlContainers() => HeaderToolbar.ActionContainers;

    void ISupportActionsToolbarVisibility.SetVisible(bool isVisible) => (CurrentWindow?.Template as ISupportActionsToolbarVisibility)?.SetVisible(isVisible);
}
